package brouter.compiler;

import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;

import brouter.annotation.Parameter;
import brouter.annotation.bean.ParameterBean;
import brouter.compiler.utils.Config;

@AutoService(Processor.class)
@SupportedAnnotationTypes({"brouter.annotation.Parameter"}) // 注解
public class ParameterProcessor extends AbstractProcessor {
    private static final String TAG = ">>>>>>>>>>>ParameterProcessor: ";
    private Filer filer;
    private Messager messager;
    private Elements elementUtils;
    private Types typeUtils;

    private String moduleName;
    private String aptPackage;

    private TypeMirror callMirror;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        elementUtils = processingEnvironment.getElementUtils();
        filer = processingEnvironment.getFiler();
        messager = processingEnvironment.getMessager();
        typeUtils = processingEnvironment.getTypeUtils();

        // 获取Gradle的传参
        Map<String, String> options = processingEnvironment.getOptions();
        moduleName = options.get(Config.MODULE_NAME);
        aptPackage = options.get(Config.APT_PACKAGE);

        callMirror = elementUtils.getTypeElement("com.breeze.base.Call").asType();
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        note("module=" + moduleName + ", aptPackage=" + aptPackage);
        Map<TypeElement, List<ParameterBean>> targetMap = getTargetMap(roundEnvironment);
        createJavaFile(targetMap);
        // 返回true，process会执行两遍，第二遍为检查机制，此时set是空
        // 返回false，process执行一次
        return true;
    }

    private Map<TypeElement, List<ParameterBean>> getTargetMap(RoundEnvironment roundEnvironment) {
        Map<TypeElement, List<ParameterBean>> targetMap = new HashMap<>();
        // 获取所有包含Parameter注解的元素，此处是VariableElement
        Set<? extends Element> annotatedElements = roundEnvironment.getElementsAnnotatedWith(Parameter.class);
        for (Element element : annotatedElements) {
            // 变量名称
            String varName = element.getSimpleName().toString();
            // 变量类型
            TypeMirror fieldType = element.asType();
            // 变量类名称
            TypeElement typeElement = (TypeElement) element.getEnclosingElement();
            List<ParameterBean> parameterBeans = targetMap.get(typeElement);
            if (parameterBeans == null) {
                parameterBeans = new ArrayList<>();
                targetMap.put(typeElement, parameterBeans);
            }
            Parameter parameter = element.getAnnotation(Parameter.class);
            // 注解name值
            String paramName = parameter.name();
            ParameterBean parameterBean = new ParameterBean(varName, fieldType, paramName);
            parameterBeans.add(parameterBean);
        }
        return targetMap;
    }


    /**
     * 类名$$Parameter类
     * public final class SecondActivity$$Parameter {
     * public static void inject(LoginActivity target) {
     * target.name = target.getIntent().getStringExtra("name");
     * }
     * }
     *
     * @param targetMap
     */
    private void createJavaFile(Map<TypeElement, List<ParameterBean>> targetMap) {

        String classSuffix = "$$Parameter";
        for (Map.Entry<TypeElement, List<ParameterBean>> typeEntry : targetMap.entrySet()) {
            TypeElement te = typeEntry.getKey();
            List<ParameterBean> parameterBeans = typeEntry.getValue();
            String className = te.getSimpleName().toString() + classSuffix;
            note(className);
            // 1. 方法
            // public void inject(LoginActivity target) {}
            MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("inject")
                    .addModifiers(Modifier.PUBLIC)
                    .addParameter(ClassName.get(te), "target");
            for (ParameterBean parameterBean : parameterBeans) {
                // 类型不同，生成的代码不同
                TypeMirror fieldType = parameterBean.getFieldType();
                if (typeUtils.isSubtype(fieldType, callMirror)) {
                    // target.loginDrawable = (LoginDrawable)RouterManager.getInstance().build("/login/getLoginDrawable").navigation(target);
                    methodBuilder.addStatement("target.$N = ($T)$T.getInstance().build($S).navigation(target)",
                            parameterBean.getName(),
                            ClassName.get(fieldType),
                            ClassName.get("brouter.api", "RouterManager"),
                            parameterBean.getParamName()
                    );
                } else {
                    // target.name = target.getIntent().getStringExtra("name");
                    methodBuilder.addStatement("$N.$N = $N.getIntent().getStringExtra($S)",
                            "target",
                            parameterBean.getName(),
                            "target",
                            parameterBean.getParamName().equals("") ? parameterBean.getName() : parameterBean.getParamName()
                    );
                }
            }

            // 2. 类
            TypeSpec typeSpec = TypeSpec.classBuilder(className)
                    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                    .addMethod(methodBuilder.build())
                    .build();

            // 3. 包
            String pkgName = elementUtils.getPackageOf(te).getQualifiedName().toString();
            JavaFile javaFile = JavaFile.builder(pkgName, typeSpec).build();
            try {
                javaFile.writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    private void note(CharSequence charSequence) {
        messager.printMessage(Diagnostic.Kind.NOTE, TAG + charSequence);
    }

    private void error(CharSequence charSequence) {
        messager.printMessage(Diagnostic.Kind.ERROR, TAG + charSequence);
    }

}
